package com.wei.czz.framework.common.handler.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2021-08-13 11:20:00
 * className RedisZSetHelper Redis-ZSet数据结构操作工具类
 * version: 1.0
 * description:
 */
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisZSetHandler {

    /*
        注意：
            1、工具类方法的参数均未做非空判断，打印操作日志可能会导致空指针异常，开发者可自行添加非空判断或者其他操作
     */

    private final Logger log = LoggerFactory.getLogger(RedisZSetHandler.class);

    private final ZSetOperations zSetOperations;

    @Autowired
    public RedisZSetHandler(RedisTemplate redisTemplate) {
        this.zSetOperations = redisTemplate.opsForZSet();
    }

    /**
     * 操作ZSet数据，向key指向数据中添加一个value值，分数是score
     * 注意:
     *  1、当key指向数据包含value时，则更新score值，但是返回添加失败结果false
     * @param key   ZSet空间键
     * @param value 添加值
     * @param score 分数值
     * @param <T>   泛型类型
     * @return 添加结果[true-成功 false-失败]
     */
    public <T> Boolean add(final String key, final T value, final double score) {
        log.info("ZSet类型 add操作 key={} value={}({}) score={}", key, value, value.getClass().getSimpleName(), score);
        return zSetOperations.add(key, value, score);
    }

    /**
     * 操作ZSet数据，向key指向数据中添加一个或者多个value-score值
     * @param key ZSet空间键
     * @param map 存储value-score值的Map对象，其中Map的key是value值，value是score值
     * @param <T> 泛型类型
     * @return 成功添加的value-score个数
     */
    public <T> Long add(final String key, Map<T, Double> map) {
        Set<DefaultTypedTuple<T>> tupleSet = map.entrySet().stream()
                .map(entry -> new DefaultTypedTuple<>(entry.getKey(), entry.getValue()))
                .collect(Collectors.toSet());
        return this.add(key, tupleSet);
    }

    /**
     * 操作ZSet数据，向key指向数据中添加一个或者多个value值及其对应的score分数值
     * 注意:
     *  1、当key指向数据包含待添加value时，则更新该value对应的score值，但是该条数据的添加结果是失败的，成功计数不增加
     * @param key ZSet空间键
     * @param set 存储value-score对的集合对象
     * @param <T> 泛型类型
     * @return 成功添加的value-score个数
     */
    public <T> Long add(final String key, final Set<DefaultTypedTuple<T>> set) {
        log.info("ZSet类型 add操作 key={} values={}", key, setToString(set));
        return zSetOperations.add(key, set);
    }

    /**
     * 操作ZSet数据，向key指向数据中添加一个value值，分数是score，当value值不存在时添加
     * @param key   ZSet空间键
     * @param value 添加值
     * @param score 分数
     * @param <T>   泛型类型
     * @return 添加结果[true-成功 false-失败]
     */
    public <T> Boolean addIfAbsent(final String key, final T value, final double score) {
        log.info("ZSet类型 addIfAbsent操作 key={} value={} score={}", key, value, score);
        return zSetOperations.addIfAbsent(key, value, score);
    }

    /**
     * 操作ZSet数据，向key指向数据中添加一个或者多个value-score值，当value值不存在时添加
     * @param key ZSet空间键
     * @param map 存储value-score值的Map对象，其中Map的key是value值，value是score值
     * @param <T> 泛型类型
     * @return 成功添加的value-score个数
     */
    public <T> Long addIfAbsent(final String key, Map<T, Double> map) {
        Set<DefaultTypedTuple<T>> tupleSet = map.entrySet().stream()
                .map(entry -> new DefaultTypedTuple<>(entry.getKey(), entry.getValue()))
                .collect(Collectors.toSet());
        return this.addIfAbsent(key, tupleSet);
    }

    /**
     * 操作ZSet数据，向key指向数据中添加一个或者多个value值及其对应的score分数值
     * 注意：
     *  1、向key指向数据添加新的value-score时，当value值不存在时，才进行添加，否则该条value-score不添加，成功计数不增加
     * @param key ZSet空间键
     * @param set 存储value-score对的集合对象
     * @param <T> 泛型类型
     * @return 成功添加的value-score个数
     */
    public <T> Long addIfAbsent(final String key, final Set<DefaultTypedTuple<T>> set) {
        log.info("ZSet类型 addIfAbsent操作 key={} values={}", key, setToString(set));
        return zSetOperations.addIfAbsent(key, set);
    }

    /**
     * 操作ZSet数据，key指向数据，value值的‘分数’加上score分数
     * 注意：
     *  1、如果key指向数据中不包含value值，则新建一个value值，并且该值的分数为score
     * @param key   ZSet空间键
     * @param value 操作值
     * @param score 分数
     * @param <T>   泛型类型
     * @return value值最新分数
     */
    public <T> Double incrementScore(final String key, final T value, final double score) {
        log.info("ZSet类型 incrementScore操作 key={} value={} score={}", key, value, score);
        return zSetOperations.incrementScore(key, value, score);
    }

    /**
     * 操作ZSet数据，统计key指向数据中分数在startScore到endScore范围内的value值个数
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key        ZSet空间键
     * @param startScore 开始分数
     * @param endScore   结束分数
     * @param <T>        泛型类型
     * @return 符合分数范围的元素个数
     */
    public <T> Long count(final String key, double startScore, double endScore) {
        // 交换startScore 和 endScore 值
        if (startScore > endScore) { double t = startScore; startScore = endScore; endScore = t; }
        log.info("ZSet类型 count操作 key={} startScore={} endScore={}", key, startScore, endScore);
        return zSetOperations.count(key, startScore, endScore);
    }

    /**
     * 操作ZSet数据，统计key指向数据中分数在startScore到endScore范围内的value值个数
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key   ZSet空间键
     * @param range 范围对象
     * @param <T>   泛型类型
     * @return 符合分数范围的元素个数
     */
    public <T> Long count(final String key, RedisZSetCommands.Range range) {
        // 交换startScore 和 endScore 值
        log.info("ZSet类型 count操作 key={} startScore={} endScore={}", key, range.getMin(), range.getMax());
        return zSetOperations.lexCount(key, range);
    }

    /**
     * 操作ZSet数据，默认按照分数值进行'升序'排序的方式，‘分页’截取key指向数据
     * 注意：
     *  1、页码page从1开始，1表示第一页，以此类推
     *  2、limit表示每页显示元素个数
     * @param key   ZSet空间键
     * @param page  页码
     * @param limit 每页显示个数
     * @param <T>   泛型类型
     * @return 分页截取数据集合
     */
    public <T> Set<T> range(final String key, long page, long limit) {
        log.info("ZSet类型 range操作 key={} page={} limit={}", key, page, limit);
        return zSetOperations.range(key, page, limit);
    }

    /**
     * 操作ZSet数据，截取key指向数据中分数在startScore至endScore范围内的元素
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key        ZSet空间键
     * @param startScore 开始分数
     * @param endScore   结束分数
     * @param <T>        泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<T> rangeByScore(final String key, double startScore, double endScore) {
        // 交换startScore 和 endScore 值
        if (startScore > endScore) { double t = startScore; startScore = endScore; endScore = t; }
        log.info("ZSet类型 rangeByScore操作 key={} startScore={} endScore={}", key, startScore, endScore);
        return zSetOperations.rangeByScore(key, startScore, endScore);
    }

    /**
     * 操作ZSet数据，截取key指向数据中分数在startScore至endScore范围内的元素，并且所有元素按照分数大小正序排序。
     * 最后根据page和limit参数对结果集合进行分页操作。
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key        ZSet空间键
     * @param startScore 开始分数
     * @param endScore   结束分数
     * @param page       页码
     * @param limit      每页显示个数
     * @param <T>        泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<T> rangeByScore(final String key, double startScore, double endScore, long page, long limit) {
        // 交换startScore 和 endScore 值
        if (startScore > endScore) { double t = startScore; startScore = endScore; endScore = t; }
        log.info("ZSet类型 rangeByScore操作 key={} startScore={} endScore={} page={} limit={}", key, startScore, endScore, page, limit);
        return zSetOperations.rangeByScore(key, startScore, endScore, page, limit);
    }

    /**
     * 操作ZSet数据，默认按照分数值进行'升序'排序的方式，‘分页’截取key指向数据，获取元素的value值和score值
     * 注意：
     *  1、页码page从1开始，1表示第一页，以此类推
     *  2、limit表示每页显示元素个数
     * @param key   ZSet空间键
     * @param page  页码
     * @param limit 每页显示个数
     * @param <T>   泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<ZSetOperations.TypedTuple<T>> rangeWithScores(final String key, long page, long limit) {
        log.info("ZSet类型 rangeWithScores操作 key={} page={} limit={}", key, page, limit);
        return zSetOperations.rangeWithScores(key, page, limit);
    }

    /**
     * 操作ZSet数据，截取key指向数据中分数在startScore至endScore范围内的元素
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key        ZSet空间键
     * @param startScore 开始分数
     * @param endScore   结束分数
     * @param <T>        泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<ZSetOperations.TypedTuple<T>> rangeByScoreWithScores(final String key, double startScore, double endScore) {
        // 交换startScore 和 endScore 值
        if (startScore > endScore) { double t = startScore; startScore = endScore; endScore = t; }
        log.info("ZSet类型 rangeByScore操作 key={} startScore={} endScore={}", key, startScore, endScore);
        return zSetOperations.rangeByScoreWithScores(key, startScore, endScore);
    }

    /**
     * 操作ZSet数据，截取key指向数据中分数在startScore至endScore范围内的元素
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key        ZSet空间键
     * @param startScore 开始分数
     * @param endScore   结束分数
     * @param <T>        泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<ZSetOperations.TypedTuple<T>> rangeByScoreWithScores(final String key, double startScore, double endScore, long page, long limit) {
        // 交换startScore 和 endScore 值
        if (startScore > endScore) { double t = startScore; startScore = endScore; endScore = t; }
        log.info("ZSet类型 rangeByScore操作 key={} startScore={} endScore={} page={} limit={}", key, startScore, endScore, page, limit);
        return zSetOperations.rangeByScoreWithScores(key, startScore, endScore, page, limit);
    }

    /**
     * 操作ZSet数据，默认按照分数值进行'降序'排序的方式，‘分页’截取key指向数据
     * 注意：
     *  1、页码page从1开始，1表示第一页
     *  2、limit表示每页显示元素个数
     * @param key   ZSet空间键
     * @param page  页码
     * @param limit 每页显示个数
     * @param <T>   泛型类型
     * @return 分页截取数据集合
     */
    public <T> Set<T> reverseRange(final String key, long page, long limit) {
        log.info("ZSet类型 reverseRange操作 key={} page={} limit={}", key, page, limit);
        return zSetOperations.reverseRange(key, page, limit);
    }

    /**
     * 操作ZSet数据，截取key指向数据中分数在startScore至endScore范围内的元素
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key        ZSet空间键
     * @param startScore 开始分数
     * @param endScore   结束分数
     * @param <T>        泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<T> reverseRangeByScore(final String key, double startScore, double endScore) {
        // 交换startScore 和 endScore 值
        if (startScore > endScore) { double t = startScore; startScore = endScore; endScore = t; }
        log.info("ZSet类型 reverseRangeByScore操作 key={} startScore={} endScore={}", key, startScore, endScore);
        return zSetOperations.reverseRangeByScore(key, startScore, endScore);
    }

    /**
     * 操作ZSet数据，截取key指向数据中分数在startScore至endScore范围内的元素
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key        ZSet空间键
     * @param startScore 开始分数
     * @param endScore   结束分数
     * @param <T>        泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<T> reverseRangeByScore(final String key, double startScore, double endScore, long page, long limit) {
        // 交换startScore 和 endScore 值
        if (startScore > endScore) { double t = startScore; startScore = endScore; endScore = t; }
        log.info("ZSet类型 rangeByScore操作 key={} startScore={} endScore={} page={} limit={}", key, startScore, endScore, page, limit);
        return zSetOperations.reverseRangeByScore(key, startScore, endScore, page, limit);
    }

    /**
     * 操作ZSet数据，默认按照分数值进行'降序'排序的方式，‘分页’截取key指向数据，获取元素的value值和score值
     * 注意：
     *  1、页码page从1开始，1表示第一页，以此类推
     *  2、limit表示每页显示元素个数
     * @param key   ZSet空间键
     * @param page  页码
     * @param limit 每页显示个数
     * @param <T>   泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<ZSetOperations.TypedTuple<T>> reverseRangeWithScores(final String key, long page, long limit) {
        log.info("ZSet类型 reverseRangeWithScores操作 key={} page={} limit={}", key, page, limit);
        return zSetOperations.reverseRangeWithScores(key, page, limit);
    }

    /**
     * 操作ZSet数据，截取key指向数据中分数在startScore至endScore范围内的元素，并且所有元素按照分数大小降序排序
     * 操作获取元素的value值和score值
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key        ZSet空间键
     * @param startScore 开始分数
     * @param endScore   结束分数
     * @param <T>        泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<ZSetOperations.TypedTuple<T>> reverseRangeByScoreWithScores(final String key, double startScore, double endScore) {
        // 交换startScore 和 endScore 值
        if (startScore > endScore) { double t = startScore; startScore = endScore; endScore = t; }
        log.info("ZSet类型 reverseRangeByScoreWithScores操作 key={} startScore={} endScore={}", key, startScore, endScore);
        return zSetOperations.reverseRangeByScoreWithScores(key, startScore, endScore);
    }

    /**
     * 操作ZSet数据，截取key指向数据中分数在startScore至endScore范围内的元素，并且所有元素按照分数大小降序排序
     * 操作获取元素的value值和score值
     * 注意：
     *  1、统计范围包含startScore和endScore值，即左闭右闭
     * @param key        ZSet空间键
     * @param startScore 开始分数
     * @param endScore   结束分数
     * @param <T>        泛型类型
     * @return 返回符合条件的元素集合
     */
    public <T> Set<ZSetOperations.TypedTuple<T>> reverseRangeByScoreWithScores(final String key, double startScore, double endScore, long page, long limit) {
        // 交换startScore 和 endScore 值
        if (startScore > endScore) { double t = startScore; startScore = endScore; endScore = t; }
        log.info("ZSet类型 reverseRangeByScoreWithScores操作 key={} startScore={} endScore={} page={} limit={}", key, startScore, endScore, page, limit);
        return zSetOperations.reverseRangeByScoreWithScores(key, startScore, endScore, page, limit);
    }

    /**
     * 操作ZSet数据，key指向数据按照其分数值大小升序排序，获取value值在列表中的下标
     * 注意：
     *  1、下标从0开始，0代表正序第一位，以此类推
     *  2、如果value值在key指向数据中不存在，则返回null
     * @param key   ZSet空间键
     * @param value 查找值
     * @param <T>   泛型类型
     * @return value值在列表中的下标
     */
    public <T> Long rank(final String key, final T value) {
        log.info("ZSet类型 rank操作 key={} value={}", key, value);
        return zSetOperations.rank(key, value);
    }

    /**
     * 操作ZSet数据，key指向数据按照其分数值大小‘降序’排序，获取value值在列表中的下标
     * 注意：
     *  1、下标从0开始，0代表降序第一位，以此类推
     *  2、如果value值在key指向数据中不存在，则返回null
     * @param key   ZSet空间键
     * @param value 查找值
     * @param <T>   泛型类型
     * @return value值在列表中的下标
     */
    public <T> Long reverseRank(final String key, final T value) {
        log.info("ZSet类型 reverseRank操作 key={} value={}", key, value);
        return zSetOperations.reverseRank(key, value);
    }

    /**
     * 操作ZSet数据，查找key指向数据中value值对应的分数
     * 注意：
     *  1、如果key指向数据中不存在value值，则返回null
     * @param key   ZSet空间键
     * @param value 查找值
     * @param <T>   泛型类型
     * @return value值对应的分数
     */
    public <T> Double score(final String key, final T value) {
        log.info("ZSet类型 score操作 key={} value={}", key, value);
        return zSetOperations.score(key, value);
    }

    /**
     * 操作ZSet数据，获取key指向数据中的元素个数
     * 注意：
     *  1、如果key不存在，则返回0
     * @param key ZSet空间键
     * @return key指向数据中的元素个数
     */
    public Long size(final String key) {
        log.info("ZSet类型 size操作 key={}", key);
        return zSetOperations.size(key);
    }

    /**
     * 操作ZSet数据，获取key指向数据中的元素个数
     * 注意：
     *  1、如果key不存在，则返回0
     * @param key ZSet空间键
     * @return key指向数据中的元素个数
     */
    public Long zCard(final String key) {
        log.info("ZSet类型 zCard操作 key={}", key);
        return zSetOperations.zCard(key);
    }

    /**
     * 操作ZSet数据，对key1指向数据和key2指向数据进行取’交集‘操作，并将操作结果存入resultKey指向数据中
     * 注意：
     *  1、key1和key2的相交元素，存入结果集时，将key1元素和key2元素的分数相加，生成最新分数，元素值不变
     *  2、如果resultKey键存在
     *   2.1、交集结果不为空，结果将覆盖旧的数据（删除所有旧数据）
     *   2.2、交集结果为空，删除resultKey指向的所有数据
     *  3、如果resultKey键不存在，且交集结果不为空，创建一个新键resultKey，并增加其指向的数据
     * @param key1      ZSet空间键
     * @param key2      ZSet空间键
     * @param resultKey 交集结果Set空间键
     * @return key1和key2指向数据的相交元素个数
     */
    public Long intersectAndStore(final String key1, final String key2, final String resultKey) {
        log.info("ZSet类型 intersectAndStore操作 key1={} key2={} resultKey={}", key1, key2, resultKey);
        return zSetOperations.intersectAndStore(key1, key2, resultKey);
    }

    /**
     * 操作ZSet数据，对key1指向数据和key2指向数据进行取’交集‘操作，并将操作结果存入resultKey指向数据中
     * 注意：
     *  1、key1和keys所有空间指向数据存在的相交元素，存入结果集时，将key1元素和所有keys键对应元素的分数相加，生成最新分数，元素值不变
     *  2、如果resultKey键存在
     *   2.1、交集结果不为空，结果将覆盖旧的数据（删除所有旧数据）
     *   2.2、交集结果为空，删除resultKey指向的所有数据
     *  3、如果resultKey键不存在，且交集结果不为空，创建一个新键resultKey，并增加其指向的数据
     * @param key1      ZSet空间键
     * @param keys      所有进行交集的Set空间键
     * @param resultKey 交集结果Set空间键
     * @return key1和keys包含的所有空间键指向数据的相交元素个数
     */
    public Long intersectAndStore(final String key1, final List<String> keys, final String resultKey) {
        log.info("ZSet类型 intersectAndStore操作 key1={} keys={} resultKey={}", key1, keys, resultKey);
        return zSetOperations.intersectAndStore(key1, keys, resultKey);
    }

    /**
     * 操作ZSet数据，对key1和key2指向数据进行取’并集‘操作，并将操作结果存入resultKey指向数据中
     *
     * @param key1      ZSet空间键
     * @param key2      ZSet空间键
     * @param resultKey 交集结果Set空间键
     * @return key1和key2取并集操作后的，resultKey指向数据的元素个数
     */
    public Long unionAndStore(final String key1, final String key2, final String resultKey) {
        log.info("ZSet类型 unionAndStore操作 key1={} key2={} resultKey={}", key1, key2, resultKey);
        return zSetOperations.unionAndStore(key1, key2, resultKey);
    }

    /**
     * 操作ZSet数据，对key1和key2指向数据进行取’并集‘操作，并将操作结果存入resultKey指向数据中
     * 注意：
     *  1、key1和keys所有空间指向数据存在的相交元素，存入结果集时，将key1元素和所有keys键对应元素的分数相加，生成最新分数，元素值不变
     *  2、如果resultKey键存在
     *   2.1、交集结果不为空，结果将覆盖旧的数据（删除所有旧数据）
     *   2.2、交集结果为空，删除resultKey指向的所有数据
     *  3、如果resultKey键不存在，且交集结果不为空，创建一个新键resultKey，并增加其指向的数据
     * @param key1      ZSet空间键
     * @param keys      ZSet空间键
     * @param resultKey 交集结果Set空间键
     * @return key1和key2取并集操作后的，resultKey指向数据的元素个数
     */
    public Long unionAndStore(final String key1, final List<String> keys, final String resultKey) {
        log.info("ZSet类型 unionAndStore操作 key1={} keys={} resultKey={}", key1, keys, resultKey);
        return zSetOperations.unionAndStore(key1, keys, resultKey);
    }

    /**
     * 操作ZSet数据，删除key指向数据中与values包含值对应的value-score数据
     * @param key    ZSet空间键
     * @param values 一个或者多个value值
     * @param <T>    泛型类型
     * @return 成功删除的value-score个数
     */
    public <T> Long remove(final String key, final T... values) {
        log.info("ZSet类型 remove操作 key={} values={}", key, values);
        return zSetOperations.remove(key, values);
    }

    /**
     * 操作ZSet数据，删除key指向数据中，按score升序排序列表下，删除startIndex下标至endIndex下标范围内的所有value-score值
     * 注意：
     *  1、startIndex和endIndex对应下标的值均会被删除，区间为左闭右闭区间，即[startIndex,endIndex]
     *  2、开始下标和结束下标输入范围为0~正无穷，开始下标取值不能小于0
     *  3、TODO 开始下标和结束下标请求参数需开发者自行校验
     * @param key        ZSet空间键
     * @param startIndex 开始下标
     * @param endIndex   结束下标
     * @return 成功删除的元素个数
     */
    public Long removeRange(final String key, long startIndex, long endIndex) {
        log.info("ZSet类型 removeRange操作 key={} startIndex={} endIndex={}", key, startIndex, endIndex);
        return zSetOperations.removeRange(key, startIndex, endIndex);
    }

    /**
     * 操作ZSet数据，删除key指向数据中，按score升序排序列表下，删除startScore分数至endScore分数范围内的所有value-score值
     * 注意：
     *  1、startScore和endScore分数对应的值均会被删除，区间为左闭右闭区间，即[startIndex,endIndex]
     *  2、开始分数不能大于结束分数，否则删除元素的个数为0
     * @param key        ZSet空间键
     * @param startScore 开始分数值
     * @param endScore   结束分数值
     * @return 成功删除的元素个数
     */
    public Long removeRangeByScore(final String key, final double startScore, final double endScore) {
        log.info("ZSet类型 removeRangeByScore操作 key={} startScore={} endScore={}", key, startScore, endScore);
        return zSetOperations.removeRangeByScore(key, startScore, endScore);
    }

    /**
     * 将Set集合转换成字符串表示
     * @param set 集合对象
     * @param <T> 泛型类型
     * @return 集合对象字符串表示
     */
    private <T> String setToString(Set<DefaultTypedTuple<T>> set) {
        StringBuilder sb = new StringBuilder("[");
        for (ZSetOperations.TypedTuple<T> typedTuple : set) {
            T value = typedTuple.getValue();
            sb.append("{value=").append(value);
            if (value != null) {
                sb.append('(').append(value.getClass().getSimpleName()).append(')');
            }
            sb.append(", score=").append(typedTuple.getScore()).append("}, ");
        }
        return sb.substring(0, sb.length() - 2) + "]";
    }
}
